# -*- coding: utf-8 -*-
import logging
import threading
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, List
from collections import deque
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, QTimer
import sqlalchemy as sa

from core.managers.alert_manager import AlertManager
from database import DatabaseManager

# 配置日志
logging.basicConfig(
    format='%(asctime)s - %(levelname)s - %(message)s',
    level=logging.INFO
)
logger = logging.getLogger(__name__)


class SchedulerManager(QObject):
    # 信号定义
    task_started = pyqtSignal(str)  # 任务ID
    task_succeeded = pyqtSignal(str, dict)  # 任务ID, 结果数据
    task_failed = pyqtSignal(str, str)  # 任务ID, 错误信息
    schedule_updated = pyqtSignal(list)  # 当前任务列表
    resource_alert = pyqtSignal(str, str)  # (报警级别, 消息内容)

    def __init__(self, max_retries=3, parent=None):
        super().__init__(parent=parent)
        self._lock = threading.Lock()  # 线程锁
        self.max_retries = max_retries  # 最大重试次数
        self.job_queue = deque(maxlen=500)  # 任务队列
        self.failed_jobs = {}  # 失败任务记录

        # 核心组件初始化
        self.alert_manager = AlertManager(parent=self)
        self.db = DatabaseManager()
        self._init_scheduler()

        # 连接信号
        self._setup_connections()

    def _setup_connections(self):
        """建立信号连接"""
        self.alert_manager.alert_triggered.connect(self._handle_alert)
        self.scheduler.add_listener(
            self._handle_job_event,
            EVENT_JOB_EXECUTED | EVENT_JOB_ERROR
        )

    def _init_scheduler(self):
        """初始化调度器配置"""
        jobstores = {
            'default': SQLAlchemyJobStore(
                engine=sa.create_engine('sqlite:///jobs.db')
            )
        }
        executors = {
            'default': ThreadPoolExecutor(5)
        }
        job_defaults = {
            'coalesce': True,
            'max_instances': 3,
            'misfire_grace_time': 300  # 5分钟容错
        }

        self.scheduler = BackgroundScheduler(
            jobstores=jobstores,
            executors=executors,
            job_defaults=job_defaults,
            timezone='Asia/Shanghai'
        )

    @pyqtSlot(str, str)
    def _handle_alert(self, level: str, message: str):
        """处理报警信号"""
        self.resource_alert.emit(level, message)
        logger.warning(f"[系统报警] {level.upper()}: {message}")

    def start(self):
        """启动调度系统"""
        with self._lock:
            if not self.scheduler.running:
                self.scheduler.start()
                self.alert_manager.start_monitoring()
                logger.info("调度系统已启动")

    def safe_shutdown(self):
        """立即关闭调度器"""
        with self._lock:
            if self.scheduler.running:
                # 立即停止不等待
                self.scheduler.shutdown(wait=False)
                logger.info("调度器已立即停止")
            if hasattr(self, 'alert_manager'):
                self.alert_manager.stop_monitoring()

    def create_task(self, interval: int, job_func: callable,
                    job_args: Optional[list] = None,
                    job_kwargs: Optional[dict] = None) -> str:
        """创建定时任务"""
        with self._lock:
            job_args = job_args or []
            job_kwargs = job_kwargs or {}
            job_kwargs.setdefault('retries', 0)

            # 构建触发器参数
            trigger_args = self._build_trigger_args(interval)

            # 创建带重试机制的包装任务
            job = self.scheduler.add_job(
                func=self._create_retry_wrapper(job_func),
                trigger='interval' if interval > 0 else 'date',
                args=job_args,
                kwargs=job_kwargs,
                **trigger_args
            )

            self._log_task_event(job.id, 'CREATED', f"间隔: {interval}分钟")
            return job.id

    def _create_retry_wrapper(self, func: callable) -> callable:
        """创建带重试机制的任务包装器"""

        def wrapper(*args, **kwargs):
            job_id = kwargs.get('job_id', 'unknown')
            retries = kwargs.get('retries', 0)

            try:
                result = func(*args, **kwargs)
                self.task_succeeded.emit(job_id, result)
                self._log_task_event(job_id, 'SUCCESS', '执行成功')
                self._clean_failed_job(job_id)
                return result
            except Exception as e:
                if retries < self.max_retries:
                    logger.warning(f"任务 {job_id} 第 {retries + 1} 次重试")
                    kwargs['retries'] = retries + 1
                    self.create_task(
                        interval=5,  # 5分钟后重试
                        job_func=func,
                        job_args=args,
                        job_kwargs=kwargs
                    )
                else:
                    self.task_failed.emit(job_id, str(e))
                    self._log_task_event(job_id, 'FAILED', f"最终失败: {str(e)}")
                    self.failed_jobs[job_id] = {
                        'error': str(e),
                        'retries': retries
                    }
                raise

        return wrapper

    def _build_trigger_args(self, interval: int) -> Dict[str, Any]:
        """构建触发器参数"""
        if interval == 0:
            return {'run_date': datetime.now() + timedelta(seconds=5)}
        elif interval > 0:
            return {'minutes': interval}
        raise ValueError("无效的间隔时间")

    def get_scheduled_jobs(self) -> List[Dict]:
        """获取当前所有计划任务"""
        with self._lock:
            return [self._format_job_info(job) for job in self.scheduler.get_jobs()]

    def _format_job_info(self, job) -> Dict:
        """格式化任务信息"""
        return {
            'id': job.id,
            'name': job.name,
            'next_run': job.next_run_time.strftime('%Y-%m-%d %H:%M:%S'),
            'interval': str(job.trigger.interval) if job.trigger else '立即',
            'status': '运行中' if not job.pending else '等待中'
        }

    def _handle_job_event(self, event):
        """处理APScheduler事件"""
        with self._lock:
            job = self.scheduler.get_job(event.job_id)
            if not job:
                return

            if event.exception:
                logger.error(f"任务 {event.job_id} 执行失败: {event.exception}")
            else:
                logger.info(f"任务 {event.job_id} 执行成功")

    def _log_task_event(self, job_id: str, status: str, message: str):
        """记录任务事件"""
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'job_id': job_id,
            'status': status,
            'message': message
        }
        self.db.insert_log('SCHEDULER', str(log_entry))
        logger.info(f"[任务日志] {log_entry}")

    def _clean_failed_job(self, job_id: str):
        """清理失败任务记录"""
        with self._lock:
            if job_id in self.failed_jobs:
                del self.failed_jobs[job_id]

    def get_failed_jobs(self) -> List[Dict]:
        """获取失败任务列表"""
        with self._lock:
            return [{
                'job_id': job_id,
                'error': info['error'],
                'retries': info['retries']
            } for job_id, info in self.failed_jobs.items()]


# 示例用法
if __name__ == '__main__':
    def demo_task():
        print("示例任务执行...")


    from PyQt5.QtWidgets import QApplication
    import sys

    app = QApplication(sys.argv)

    scheduler = SchedulerManager()
    scheduler.start()

    # 添加每小时执行的任务
    job_id = scheduler.create_task(1, demo_task)
    print("当前任务:", scheduler.get_scheduled_jobs())

    try:
        sys.exit(app.exec_())
    except KeyboardInterrupt:
        scheduler.safe_shutdown()